//decltype(expression)

#include<iostream>
#include<string>

using namespace std;

template<typename R, typename T, typename U>
R add(T x, U y) {
    return x + y;
}

//tail type inference
template<typename T, typename U>
auto add1(T x, U y) -> decltype(x+y) {
    return x + y;
}

template<typename T, typename U>
auto add2(T x, U y) {
    return x + y;
}

int main() {
    //calculate the type
    auto x = 1;
    auto y = 2;
    decltype(x+y) z;
    if(is_same<decltype(x), int>::value) {
        cout<<"type x == int" <<endl;
    }
    if (is_same<decltype(x), float>::value)
    {
        cout<<"type x == float" <<endl;;
    }
    if (is_same<decltype(x), decltype(z)>::value)
    {
        cout<<"type x == type z" <<endl;;
    }
    //
    auto w = add1<int, double>(1, 2.0);
    if(is_same<decltype(w), double>::value)
    cout<<"w is double";
    cout<<w<<endl;

    auto q = add2<double, int>(1.0, 2);
    cout<<"q: "<<q<<endl;
    return 0;
}

string lookup1();
string& lookup2();

string  look_up_a_string_1() {
    return lookup1();
}
string& look_up_a_string_2() {
    return lookup2();
}

decltype(auto) look_up_a_string_3() {
    return lookup1();
}

decltype(auto) look_up_a_string_4() {
    return lookup2();
}